extends Object


class_name IComponent
"""
组件工具的公共接口,提供一些通用的组件组合能力
"""


const KEY_IS_FROZEN = "is_frozen"
const KEY_IS_ENABLE = "is_enable"


# 在子节点中查找
static func find_child(node: Node, find_if: Callable, optional_node: Node = null) -> Node:
    if optional_node:
        return optional_node
    for c in node.get_children():
        if find_if.call(c):
            return c
    return null

# 在子节点中查找,未找到则从指定对象复制一份
static func find_or_copy(node: Node, find_if: Callable, copy_child: Node) -> Node:
    var child: Node = find_child(node, find_if, null)
    if child == null:
        child = copy_child.duplicate()
        node.add_child(child)
    return child

# 在兄弟节点中查找
static func find_brother(node: Node, find_if: Callable, optional_node: Node = null) -> Node:
    return find_child(node.get_parent(), find_if, optional_node)

# 在父节点中查找
static func find_parent(node: Node, find_if: Callable, optional_node: Node = null, limit_deep: int = 0) -> Node:
    if optional_node:
        return optional_node
    var deep: int = 0
    var n: Node = node
    while true:
        var p: Node = n.get_parent()
        if p == null:
            break
        if find_if.call(p):
            return p

        deep += 1
        if limit_deep > 0 and deep >= limit_deep:
            break
        n = p
    return null

# 向上查找,优先父节点,再查找兄弟节点
static func find_up_nearby(node: Node, find_if: Callable, optional_node: Node = null, limit_deep: int = 1) -> Node:
    var p: Node = find_parent(node, find_if, optional_node, limit_deep)
    if p:
        return p
    return find_brother(node, find_if, optional_node)

# 向下查找,优先子节点,再向上查找对应层级的兄弟节点
static func find_down_nearby(node: Node, find_if: Callable, optional_node: Node = null, limit_deep: int = 1) -> Node:
    var n: Node = find_child(node, find_if, optional_node)
    if n:
        return n
    var deep: int = 0
    var p: Node = node
    while deep < limit_deep and p:
        n = find_brother(p, find_if, optional_node)
        if n:
            return n
        deep += 1
        p = p.get_parent()
    return null

# 先向上移动结点再向下查找
static func find_down_relative(up_deep: int, node: Node, find_if: Callable, optional_node: Node = null, limit_deep: int = 1) -> Node:
    var p: Node = node
    for i in range(up_deep):
        if p.get_parent():
            p = p.get_parent()
        else:
            break
    return find_down(p, find_if, optional_node, limit_deep)

# 查找所有符合条件的子节点
static func find_children(node: Node, find_if: Callable, children: Array, limit_count: int = 0) -> Array:
    var count: int = children.size()
    if count < 1:
        for c in node.get_children():
            if find_if.call(c):
                children.append(c)

                count += 1
                # 达到限制
                if limit_count > 0 and count >= limit_count:
                    break
    return children

# 递归查找第一个符合条件的子组件
static func find_down(node: Node, find_if: Callable, optional_node: Node = null, limit_deep: int = 0) -> Node:
    if optional_node:
        return optional_node
    var deep: int = 0
    var children: Array = node.get_children()
    var sub_children: Array = []
    while true:
        for c in children:
            if find_if.call(c):
                return c
            sub_children.append(c)
        deep += 1
        if limit_deep > 0 and deep >= limit_deep:
            return null
        if sub_children.size() < 1:
            break
        children.clear()
        for c in sub_children:
            children.append_array(c.get_children())
        sub_children.clear()
    return null

# 递归查找第一个符合条件的兄弟或父叔组件
static func find_up(node: Node, find_if: Callable, optional_node: Node = null, limit_deep: int = 0) -> Node:
    if optional_node:
        return optional_node
    var deep: int = 0
    var parent_list: Array = [node.get_parent()]
    var uncle_list: Array = []
    while true:
        for p in parent_list:
            if not p:
                continue
            for c in p.get_children():
                if find_if.call(c):
                    return c
            uncle_list.append(p)
        deep += 1
        if limit_deep > 0 and deep >= limit_deep:
            return null
        if uncle_list.size() < 1:
            break
        parent_list.clear()
        for n in uncle_list:
            parent_list.append(n.get_parent())
        uncle_list.clear()
    return null

# 返回节点是否是Component的基本组件
static func is_component(node: Node) -> bool:
    return node is IBasicComponent or node is IArea2DComponent or node is INode2DComponent or node is IUIComponent

# 在check_dict中迭代
static func check_dict_iterator(node: Node, check_dict: Dictionary, check_cb: Callable) -> void:
    var node_fields: Array = NodeHelper.get_custom_fields(node)
    for k in check_dict:
        var check_v: Variant = check_dict[k]
        var fp_check: Variant = null
        var is_required: bool = true
        var optional_v: Variant = null
        if check_v is Array:
            if check_v.size() > 0:
                fp_check = check_v[0]
                is_required = false
                # 候选值
                if check_v.size() > 1:
                    optional_v = check_v[1]
        else:
            fp_check = check_v
        if fp_check != null and fp_check is Callable:
            var is_property: bool = k in node_fields
            check_cb.call(k, is_property, node.get(k) if is_property else null, fp_check, is_required, optional_v)
        else:
            push_error("check_export callable type is error:%s - %s" % [k, node])

# 适配check_exports接口
static func merge(check_dict: Dictionary, super_check_dict: Dictionary, ignore_super_checks: Array[String] = []) -> Dictionary:
    for k in super_check_dict:
        if k in ignore_super_checks or k in check_dict:
            continue
        check_dict[k] = super_check_dict[k]
    return check_dict

# 返回当前节点是否为冻结中的状态
static func is_node_property_enable(node: Node, property: String) -> bool:
    var v: Variant = node.get(property)
    if v and (v as bool):
        return true
    return false

# 返回节点是否为冻结中(可继承父节点属性)
static func is_property_enable(node: Node, property: String) -> bool:
    var n: Node = node
    while true:
        if is_node_property_enable(n, property):
            return true
        n = n.get_parent()
        if n == null:
            return false
    return false

# 返回函数是否为IComponent设置接口
static func is_set_method(check_method: Callable) -> bool:
    return check_method.get_bound_arguments_count() > 0

# 返回是否附带校验的check选项
static func is_check_required(check_v: Variant) -> bool:
    return check_v and check_v is Array

# 获取附带的check自定义接口
static func get_check_method(check_v: Variant) -> Callable:
    if is_check_required(check_v):
        if check_v.size() > 0:
            return check_v[0]
        else:
            return Callable()
    return check_v

# 清楚属性
static func clear_property(node: Node) -> void:
    check_dict_iterator(node, node.check_exports(),
        func(k: String, is_property: bool, v: Variant, check_method: Callable, is_required: bool, optional_value: Variant):
            if is_property and is_set_method(check_method):
                if v is Array:
                    v.clear()
                else:
                    node.set(k, null)
    )

# 设置节点的初始化数据
static func fill_property(node: Node) -> bool:
    var is_success: bool = true
    check_dict_iterator(node, node.__check_exports(),
        func(k: String, is_property: bool, v: Variant, check_method: Callable, is_required: bool, optional_value: Variant):
            # 只有组件依赖的属性校验项才进行自动初始化赋值
            if is_property and not v and is_set_method(check_method):
                var find_v: Variant = check_method.call()
                if find_v:
                    node.set(k, find_v)
                else:
                    if is_required:
                        is_success = false
                    else:
                        # 非必填项,获取失败则设置为候选值
                        node.set(k, optional_value)
    )
    return is_success

# 批量绑定事件
static func onhooks(node: Node) -> void:
    var hooks: Dictionary = node.__init_hooks()
    for n in hooks:
        n.connect(hooks[n])

# 批量注销事件
static func unhooks(node: Node) -> void:
    var hooks: Dictionary = node.__init_hooks()
    for n in hooks:
        n.disconnect(hooks[n])

# 更新hook绑定
static func update_hook(current: Node, new_value: Node, e_name: String, hook: Callable) -> Variant:
    var e_c: Variant = current.get(e_name) if current else null
    var e_v: Variant = new_value.get(e_name) if new_value else null
    if e_c and e_c is Signal:
        e_c.disconnect(hook)
    if e_v and e_v is Signal:
        e_v.connect(hook)
    return new_value

static func prepare(node: Node) -> bool:
    var is_success: bool = true
    if not is_property_enable(node, KEY_IS_FROZEN):
        is_success = fill_property(node)
        if is_success:
            onhooks(node)
    # 如果数据未就绪,禁用组件,防止process中报错
    node.is_node_frozen = node.is_frozen or not is_success
    if node.is_node_frozen:
        push_warning("node is frozen:", node)
    return is_success

static func reset(node: Node) -> void:
    var check_dict: Dictionary = node.__check_exports()
    for k in check_dict:
        var v: Variant = node.get(k)
        if v:
            if v is Array:
                v.clear()
            else:
                node.set(k, null)
    unhooks(node)
    node.is_node_frozen = false

static func editor_warnings(node: Node) -> Array:
    var check_dict: Dictionary = node.__check_exports()
    for property in check_dict:
        var check_value: Variant = check_dict[property]
        # 非必填项
        if check_value is Array:
            continue
        var check_method: Callable = check_value as Callable
        if not check_method.is_null() and not check_method.call():
            return ["please check whether the '{0}' is valid".format([property])]
    return []

static func is_available(node: Node) -> bool:
    if Engine.is_editor_hint() or node.is_node_frozen:
        return false
    return node.is_enable

static func set_enable_process(node: Node, enable: bool = true) -> bool:
    var is_enable: bool = is_available(node) and enable
    node.set_process(is_enable)
    node.set_physics_process(is_enable)
    return is_enable

static func init(node: Node) -> bool:
    if editor_warnings(node).size() > 0:
        return false
    return fill_property(node)

static func ready(node: Node) -> void:
    node.is_node_frozen = is_property_enable(node, KEY_IS_FROZEN)
    if not node.is_node_frozen:
        var errors: Array = editor_warnings(node)
        if errors.size() < 1:
            node.__prepare()
        else:
            # 逻辑上进行冻结
            node.is_node_frozen = true
            push_error("'%s' _ready() error:%s" % [node, errors[0]])
            print(node.get_parent())
            node.print_tree_pretty()
    set_enable_process(node)
